home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / Snippets / Devices / SCSI Simple Sample / Src / OriginalSCSI.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-24  |  17.3 KB  |  474 lines  |  [TEXT/KAHL]

  1. /*                                OriginalSCSI.c                                    */
  2. /*
  3.  * OriginalSCSI.c
  4.  * Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
  5.  *
  6.  * Talk to the Macintosh SCSI Handler using the "old" interface as defined in
  7.  * Inside Mac IV. This is a synchronous call that executes a single SCSI Command
  8.  * on a device. It does not support multiple busses or logical units.
  9.  *
  10.  * Calling Sequence:
  11.  *        OSErr                OriginalSCSI(
  12.  *                short                    targetID,
  13.  *                const SCSI_CommandPtr    scsiCommand,
  14.  *                unsigned short            cmdBlockLength,
  15.  *                Boolean                    writeToDevice,
  16.  *                Ptr                        bufferPtr,
  17.  *                unsigned long            transferSize,
  18.  *                unsigned long            transferQuantum,
  19.  *                unsigned long            completionTimeout,
  20.  *                unsigned short            *stsBytePtr,
  21.  *                unsigned short            *msgBytePtr
  22.  *                unsigned long            *actualTransferCount
  23.  *            );
  24.  * The parameters have the following meaning:
  25.  *
  26.  *    targetID            The SCSI Bus ID of the target (0 .. 6). Note that this
  27.  *                        function can only access LUN zero.
  28.  *    scsiCommand            The SCSI Command Block (6, 10, or 12 bytes). The command
  29.  *                        block length will be determined by examining the command
  30.  *                        parameter. This is standardized for all but
  31.  *                        "vendor-specific" commands.
  32.  *    writeToDevice        TRUE if this command writes to the device. FALSE if this
  33.  *                        command reads from the device or does not require a data
  34.  *                        phase.
  35.  *    bufferPtr            The user data buffer for Read/Write commands. It should be
  36.  *                        NULL if a data transfer phase is not used for this command.
  37.  *                        (e.g. for Test Unit Ready).
  38.  *    transferSize        The total number of bytes to transfer to or from the device.
  39.  *    transferQuantum        This is needed to configure the transfer information block
  40.  *                        (TIB). The following values are appropriate:
  41.  *                        -- Set to zero for a one-shot blind transfer.
  42.  *                            ActualTransferCount is not correctly returned.
  43.  *                        -- Set to one if a polled transfer is needed. This is
  44.  *                            useful for Request Sense or other management requests,
  45.  *                            especially requests with a variable-length record. On
  46.  *                            return, the actual number of bytes that were transferred
  47.  *                            will be in the actualTransferCount variable.
  48.  *                        -- Set to the transferSize value for a normal, "Blind"
  49.  *                            transfer. This is the normal case for data requests.
  50.  *                            ActualTransferCount will equal transferSize on success,
  51.  *                            but does not correctly return the actual count on phase
  52.  *                            errors. This, however, can be recovered from the Request
  53.  *                            Sense record.
  54.  *                        -- Set to a sub-multiple (such as a block length) for
  55.  *                            requests that need re-synchronization between sectors.
  56.  *                        -- Other values will likely result in errors.
  57.  *    completionTimeout    The timeout (in Ticks) for the command. This should be short
  58.  *                        for disks, but must be long for tape devices and some setup
  59.  *                        requests, such as Mode Select.
  60.  *    stsBytePtr            This short is set to the byte returned in the device's
  61.  *                        Status Phase.
  62.  *    msgBytePtr            This short is set to the byte returned in the device's
  63.  *                        Command Complete message.
  64.  *    actualTransferCount    This will be set to the number of "cycles" through the TIB
  65.  *                        loop times the transferCount. This should equal the number
  66.  *                        of bytes transferred if transferCount is set to one.
  67.  *                        (Ignored if NULL.)
  68.  * Return codes:
  69.  *    noErr            normal
  70.  *    scCommErr        no such device (selection error)
  71.  *    scPhaseErr        user data buffer was the wrong size for the transfer.
  72.  *                    Look at the status byte to see if this is a problem: 
  73.  *                    you may merely have given a large buffer size to
  74.  *                    a variable-length request, such as Device Inquiry.
  75.  *    sc...            other SCSI error.
  76.  *    statusErr        Device returned "Check condition." The caller should
  77.  *                    issue a Request Sense SCSI Command to this device.
  78.  *    controlErr        Device returned "Busy"
  79.  *    ioErr            Other (serious) device status. The caller should
  80.  *                    examine the Status and Message bytes to determine
  81.  *                    the problem.
  82.  *    paramErr        Could not determine the command length.
  83.  */
  84. #include <scsi.h>
  85. #include <Errors.h>
  86. #include <GestaltEqu.h>
  87. #include <Memory.h>
  88. #include <Events.h>
  89. #include "MacSCSICommand.h"
  90. #ifndef TRUE
  91. #define FALSE        0
  92. #define TRUE        1
  93. #endif
  94.  
  95. /*
  96.  * Execute a SCSI command using the original (Inside Mac IV) SCSI Manager.
  97.  * Return codes:
  98.  *
  99.  *    noErr            normal
  100.  *    paramErr        could not determine command length from command
  101.  *    scCommErr        could not select this device or bus busy
  102.  *    sc...            other scsi error
  103.  *    statusErr        Device returned "Check condition"
  104.  *    controlErr        Device returned "Busy" (Note: device error)
  105.  *    ioErr            Other (serious) device status -- bug.
  106.  */
  107. OSErr                        OriginalSCSI(
  108.         short                    targetID,            /* Device ID on this bus    */
  109.         const SCSI_CommandPtr    scsiCommand,        /* The actual scsi command    */
  110.         unsigned short            cmdBlockLength,        /* -> Length of CDB            */
  111.         Boolean                    writeToDevice,        /* TRUE to write            */
  112.         Ptr                        bufferPtr,            /* -> user data buffer        */
  113.         unsigned long            transferSize,        /* How much to transfer        */
  114.         unsigned long            transferQuantum,    /* TIB setup parameter        */
  115.         unsigned long            completionTimeout,    /* Ticks to wait            */
  116.         unsigned short            *stsBytePtr,        /* <- status phase byte        */
  117.         unsigned short            *msgBytePtr,        /* <- cmd complete messge    */
  118.         unsigned long            *actualTransferCount
  119.     );
  120.  
  121. /*
  122.  * SCSI command status (from status phase)
  123.  */
  124. #define     kScsiStatusGood            0x00            /* Normal completion        */
  125. #define     kScsiStatusCheckCondition    0x02            /* Need GetExtendedStatus    */
  126. #define     kScsiStatusConditionMet    0x04            /* For Compare Command?        */
  127. #define     kScsiStatusBusy            0x08            /* Device busy (self-test?)    */
  128. #define     kScsiStatusIntermediate    0x10            /* Intermediate status        */
  129. #define     kScsiStatusResConflict        0x18            /* Reservation conflict        */
  130. #define     kScsiStatusQueueFull        0x28            /* Target can't do command    */
  131. #define     kScsiStatusReservedMask    0x3e            /* Vendor specific?            */
  132.  
  133. /*
  134.  * This is the maximum number of times we try to grab the SCSI Bus
  135.  */
  136. #define kMaxSCSIRetries                40                /* 10 seconds, 4 times/sec    */
  137. /*
  138.  * This test is TRUE if the SCSI bus status indicates "busy" (which is the case
  139.  * if either the BSY or SEL bit is set).
  140.  */
  141. #ifndef kScsiStatBSY
  142. #define kScsiStatBSY                (1 << 6)
  143. #endif
  144. #ifndef kScsiStatSEL
  145. #define kScsiStatSEL                (1 << 1)
  146. #endif
  147. #define ScsiBusBusy()        ((SCSIStat() & (kScsiStatBSY | kScsiStatSEL)) != 0)
  148.  
  149. static void        NextFunction(void);        /* Dummy function for OriginalSCSI size    */
  150. static Boolean    IsVirtualMemoryRunning(void);
  151. /*
  152.  * These are bitmasks for the vmHoldMask variable. A bit is set if its associated
  153.  * memory element has been held in protected (non-paged) memory.
  154.  */
  155. #define kHoldFunction            0x0001                /* AsyncSCSI function code    */
  156. #define kHoldStack                0x0002                /* Local variables            */
  157. #define kHoldUserBuffer            0x0004                /* User data buffer, if any    */
  158. #define kHoldCommandBlock        0x0008                /* SCSI Command Data Block    */
  159.  
  160. /*
  161.  * Execute a SCSI command.
  162.  * Returns the final status as noted above.
  163.  */
  164. OSErr
  165. OriginalSCSI(
  166.         short                    targetID,            /* SCSI device on this bus    */
  167.         const SCSI_CommandPtr    scsiCommand,        /* The actual scsi command    */
  168.         unsigned short            cmdBlockLength,        /* -> Length of CDB            */
  169.         Boolean                    writeToDevice,        /* TRUE to write            */
  170.         Ptr                        bufferPtr,            /* -> user data buffer        */
  171.         unsigned long            transferSize,        /* How much to transfer        */
  172.         unsigned long            transferQuantum,    /* TIB setup parameter        */
  173.         unsigned long            completionTimeout,    /* Ticks to wait            */
  174.         unsigned short            *stsBytePtr,        /* <- status phase byte        */
  175.         unsigned short            *msgBytePtr,        /* <- cmd complete messge    */
  176.         unsigned long            *actualTransferCount
  177.     )
  178. {
  179.         OSErr                    status;                /* Final status                */
  180.         OSErr                    completionStatus;    /* Status from ScsiComplete    */
  181.         short                    totalTries;            /* Get/Select retries        */
  182.         short                    getTries;            /* Get retries                */
  183.         short                    iCount;                /* Bus free counter            */
  184.         unsigned long            watchdog;            /* Timeout after this        */
  185.         unsigned long            myTransferCount;    /* Gets TIB loop counter    */
  186.         /*
  187.          * The TIB has the following format:
  188.          *    [0]    scInc    user buffer            transferQuantum or transferSize
  189.          *    [1] scAdd    &theTransferCount    1
  190.          *    [2] scLoop    -> tib[0]            transferSize / transferQuantum
  191.          *    [3] scStop
  192.          * The intent of this is to return, in actualTransferCount, the number
  193.          * of times we cycled through the tib[] loop. This will be the actual
  194.          * transfer count if transferQuantum equals one, or the number of
  195.          * "blocks" if transferQuantum is the length of one sector.
  196.          */
  197.         SCSIInstr                tib[4];                /* Current TIB                */
  198.         /*
  199.          * The following parameters are used to manage virtual memory. The code
  200.          * is taken from the DTS SCSI Sample Driver.
  201.          */
  202.         unsigned short            vmHoldMask;
  203.         unsigned long            vmFunctionSize;
  204.         char                    *vmProtectedStackBase;    /* Last local variable    */
  205. /*
  206.  * These values are used to compute the size of the stack that we must hold in
  207.  * protected (non-virtual) memory. kSCSIManagerStackEstimate is an estimate.
  208.  */
  209. #define kSCSILocalVariableSize    ( \
  210.         (unsigned long) (((Ptr) &status) - ((Ptr) &vmProtectedStackBase))    \
  211.     )
  212. #define kSCSIManagerStackEstimate 512
  213. #define kSCSIProtectedStackSize (kSCSIManagerStackEstimate + kSCSILocalVariableSize)
  214.  
  215.         status = noErr;
  216.         vmHoldMask = 0;
  217.         /*
  218.          * If there is a data transfer, setup the tib.
  219.          */
  220.         myTransferCount = 0;
  221.         if (transferQuantum == 0)
  222.             transferQuantum = transferSize;
  223.         if (bufferPtr != NULL) {
  224.             tib[0].scOpcode = scInc;
  225.             tib[0].scParam1 = (unsigned long) bufferPtr;
  226.             tib[0].scParam2 = transferQuantum;
  227.             tib[1].scOpcode = scAdd;
  228.             tib[1].scParam1 = (unsigned long) &myTransferCount;
  229.             tib[1].scParam2 = transferQuantum;
  230.             tib[2].scOpcode = scLoop;
  231.             tib[2].scParam1 = (-2 * sizeof (SCSIInstr));
  232.             tib[2].scParam2 = transferSize / tib[0].scParam2;
  233.             tib[3].scOpcode = scStop;
  234.             tib[3].scParam1 = 0;
  235.             tib[3].scParam2 = 0;
  236.         }
  237.         if (IsVirtualMemoryRunning()) {
  238.  
  239.             /*
  240.              * Virtual memory is active. Lock all of the memory segments that we
  241.              * need in "real" memory (i.e. non-paged pool) for the duration of the
  242.              * call. Since  we need to back out of VM holds if there are errors,
  243.              * we'll use bits in vmHoldMask to record the status of our attempts.
  244.              *
  245.              * Note: in a real application or driver, the user buffers should be
  246.              * held outside of the SCSI Manager code:
  247.              *        HoldMemory(data buffer);
  248.              *        HoldMemory(autosense buffer);
  249.              *        status = CallSCSIManager(...);
  250.              *        UnholdMemory(...);
  251.              *
  252.              * First, hold the MacSCSI function. It starts at AsyncSCSI and
  253.              * extends to the start of the next function. This is marked by a
  254.              * dummy function. The left-margin comments indicate the value
  255.              * of vmHoldCount if the indicated HoldMemory succeeded. This is not
  256.              * needed for drivers.
  257.              */
  258.             vmFunctionSize =
  259.                 (unsigned long) NextFunction - (unsigned long) OriginalSCSI;
  260.             status = HoldMemory(OriginalSCSI, vmFunctionSize);
  261.             if (status == noErr)
  262.                 vmHoldMask |= kHoldFunction;
  263.             if (status == noErr) {
  264.                 /*
  265.                  * Hold a chunk of stack space to call the SCSI Manager and to
  266.                  * protect our local variables. This is always needed, as drivers
  267.                  * can be called from application contexts.
  268.                  */
  269.                 vmProtectedStackBase =
  270.                     (char *) &vmProtectedStackBase - kSCSIManagerStackEstimate;
  271.                 status = HoldMemory(vmProtectedStackBase, kSCSIProtectedStackSize);
  272.                 if (status == noErr)
  273.                     vmHoldMask |= kHoldStack;
  274.             }
  275.             if (status == noErr) {
  276.                 /*
  277.                  * Lock down the command block. In this sample, we allocated
  278.                  * the command block in the application heap. A driver would
  279.                  * typically allocate it in the System Heap and, hence, would
  280.                  * not require this call.
  281.                  */
  282.                 status = HoldMemory((Ptr) scsiCommand, cmdBlockLength);
  283.                 if (status == noErr)
  284.                     vmHoldMask |= kHoldCommandBlock;
  285.             }        
  286.             if (status == noErr && bufferPtr != NULL) {
  287.                 /*
  288.                  * Lock down the user buffer, if any. In a real-world application
  289.                  * or driver, this would be done before calling the SCSI interface.
  290.                  */
  291.                 status = HoldMemory(bufferPtr, transferSize);
  292.                 if (status != noErr)
  293.                     vmHoldMask |= kHoldStack;
  294.             }
  295.             if (status != noErr)
  296.                 goto exit;
  297.         }
  298.         /*
  299.          * Arbitrate for the scsi bus.  This will fail if some other device is
  300.          * accessing the bus at this time (which is unlikely).
  301.          *
  302.          *** Do not set breakpoints or call any functions that may require device
  303.          *** I/O (such as display code that accesses font resources between
  304.          *** SCSIGet and SCSIComplete,
  305.          *
  306.          */
  307.         for (totalTries = 0; totalTries < kMaxSCSIRetries; totalTries++) {
  308.             for (getTries = 0; getTries < 4; getTries++) {
  309.                 /*
  310.                  * Wait for the bus to go free.
  311.                  */
  312.                 watchdog = TickCount() + 300;        /* 5 second timeout            */
  313.                 while (ScsiBusBusy()) {
  314.                     if (TickCount() > watchdog) {
  315.                         status = scArbNBErr;
  316.                         goto exit;
  317.                     }
  318.                 }
  319.                 /*
  320.                  * The bus is free, try to grab it
  321.                  */
  322.                 for (iCount = 0; iCount < 4; iCount++) {
  323.                     if ((status = SCSIGet()) == noErr)
  324.                         break;
  325.                 }
  326.                 if (status == noErr)
  327.                     break;                            /* Success: we have the bus */
  328.                 /*
  329.                  * The bus became busy again. Try to wait for it to go free.
  330.                  */
  331.                 for (iCount = 0; iCount < 100 && ScsiBusBusy(); iCount++)
  332.                     ;
  333.             } /* The getTries loop */
  334.             if (status != noErr) {
  335.                 /*
  336.                  * The SCSI Manager thinks the bus is not busy and not selected,
  337.                  * but "someone" has set its internal semaphore that signals
  338.                  * that the SCSI Manager itself is busy. The application will have
  339.                  * to handle this problem. (We tried getTries * 4 times).
  340.                  */
  341.                 goto exit;
  342.             }
  343.             /*
  344.              * We now own the SCSI bus. Try to select the device.
  345.              */
  346.             if ((status = SCSISelect(targetID)) != noErr)
  347.                 goto exit;
  348.             /*
  349.              * From this point on, we must exit through SCSIComplete() even if an
  350.              * error is detected. Send a command to the selected device. There are
  351.              * several failure modes, including an illegal command (such as a
  352.              * write to a read-only device). If the command failed because of
  353.              * "device busy", we will try it again.
  354.              */
  355.             status = SCSICmd((Ptr) scsiCommand, cmdBlockLength);
  356.             if (status == noErr && bufferPtr != NULL) {
  357.                 /*
  358.                  * This command requires a data transfer.
  359.                  */
  360.                 if (writeToDevice) {
  361.                     if (transferQuantum == 1)
  362.                         status = SCSIWrite((Ptr) tib);
  363.                     else {
  364.                         status = SCSIWBlind((Ptr) tib);
  365.                     }
  366.                 }
  367.                 else {
  368.                     if (transferQuantum == 1)
  369.                         status = SCSIRead((Ptr) tib);
  370.                     else {
  371.                         status = SCSIRBlind((Ptr) tib);
  372.                     }
  373.                 }
  374.             }
  375. finish:
  376.             /*
  377.              * SCSIComplete "runs" the bus-phase algorithm until the bitter end,
  378.              * returning the status and command-completion message bytes..
  379.              */
  380.             completionStatus = SCSIComplete(
  381.                         (short *) stsBytePtr,
  382.                         (short *) msgBytePtr,
  383.                         completionTimeout
  384.                     );
  385.             /*
  386.              * If we have an error here, return as the "final" status.
  387.              * 
  388.              */
  389.             if (completionStatus != noErr)
  390.                 status = completionStatus;
  391.             else {
  392.                 /*
  393.                  * ScsiComplete is happy. If the device is busy, Pause for 1/4
  394.                  * second and try again.
  395.                  */
  396.                 if (*stsBytePtr == kScsiStatusBusy) {
  397.                     watchdog = TickCount() + 15;
  398.                     while (TickCount() < watchdog)
  399.                         ;
  400.                     continue;                /* Do next totalTries attempt        */
  401.                 }
  402.             }
  403.             /*
  404.              * This is the normal exit (success) or final failure exit.
  405.              */
  406.             break;
  407.         } /* totalTries loop */
  408. exit:
  409.         /*
  410.          * If we held memory, unhold it now.  We ignore UnholdMemory errors:
  411.          * there isn't much we can do about them. Note that this must be
  412.          * done by driver or asynchronous completion routines.
  413.          */
  414.         if ((vmHoldMask & kHoldUserBuffer) != 0)
  415.             (void) UnholdMemory(bufferPtr, transferSize);
  416.         if ((vmHoldMask & kHoldCommandBlock) != 0)
  417.             (void) UnholdMemory((Ptr) scsiCommand, cmdBlockLength);
  418.         if ((vmHoldMask & kHoldStack) != 0)
  419.             (void) UnholdMemory(vmProtectedStackBase, kSCSIProtectedStackSize);
  420.         if ((vmHoldMask & kHoldFunction) != 0)
  421.             (void) UnholdMemory(OriginalSCSI, vmFunctionSize);
  422.         /*
  423.          * Return the number of bytes transferred to the caller. If the caller
  424.          * supplied an actual count and the count is no greater than the maximum,
  425.          * ignore any phase errors.
  426.          */
  427.         if (actualTransferCount != NULL) {
  428.             *actualTransferCount = myTransferCount;
  429.             if (status == scPhaseErr
  430.              && writeToDevice == FALSE
  431.              && myTransferCount <= transferSize
  432.              && myTransferCount > 0)
  433.                 status = noErr;
  434.         }
  435.         /*
  436.          * Return an artificial error if the device returns a non-zero status:
  437.          *    statusErr        Caller should issue RequestSense.
  438.          *    controlErr        Device is busy (self-test?) try again later.
  439.          *    ioErr            Something is dreadfully wrong.
  440.          *    scPhaseErr        This may not be a "real" error -- it may mean that the
  441.          *                    user data buffer was too large for the transfer. (See
  442.          *                    the check above.)
  443.          * Also, there is a bug in the combination of System 7.0.1 and the 53C96
  444.          * that may cause the real SCSI Status Byte to be in the Message byte.
  445.          */
  446.         if (*stsBytePtr == kScsiStatusGood
  447.          && *msgBytePtr == kScsiStatusCheckCondition)
  448.             *stsBytePtr = kScsiStatusCheckCondition;
  449.         if (status == noErr) {
  450.             switch (*stsBytePtr) {
  451.             case kScsiStatusGood:                                    break;
  452.             case kScsiStatusCheckCondition:    status = statusErr;        break;
  453.             case kScsiStatusBusy:            status = controlErr;    break;
  454.             default:                        status = ioErr;            break;
  455.             }
  456.         }
  457.         return (status);
  458. }
  459.  
  460. static void NextFunction(void) { }    /* Dummy function for OriginalSCSI size    */
  461.  
  462. static Boolean
  463. IsVirtualMemoryRunning(void)
  464. {
  465.         OSErr                        status;
  466.         long                        response;
  467.         
  468.         status = Gestalt(gestaltVMAttr, &response);
  469.         /*
  470.          * VM is active iff Gestalt succeeded and the response is appropriate.
  471.          */
  472.         return (status == noErr && ((response & (1 << gestaltVMPresent)) != 0));
  473. }
  474.